import os
import cv2
import numpy as np
from flask import Flask, render_template, request
from werkzeug.utils import secure_filename
import urllib.request

# 初始化Flask应用
app = Flask(__name__)
app.config['SECRET_KEY'] = 'your-secret-key-here'

# 配置上传文件夹
UPLOAD_FOLDER = 'static/uploads'
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
app.config['UPLOAD_FOLDER'] = UPLOAD_FOLDER

# 确保上传文件夹存在
os.makedirs(UPLOAD_FOLDER, exist_ok=True)

def allowed_file(filename):
    """检查文件扩展名是否允许"""
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS

def estimate_gender_from_features(face_roi_gray, face_features):
    """根据面部特征估计性别（基于特征统计）"""
    if len(face_features['eyes']) >= 2:
        # 计算眼睛间距与脸宽的比率
        eye_centers = []
        for (ex, ey, ew, eh) in face_features['eyes'][:2]:
            eye_centers.append((ex + ew//2, ey + eh//2))
        
        if len(eye_centers) == 2:
            eye_distance = abs(eye_centers[1][0] - eye_centers[0][0])
            face_width = face_roi_gray.shape[1]
            ratio = eye_distance / face_width
            
            # 男性通常有更宽的眼距
            if ratio > 0.35:
                return "Male", 0.7
            else:
                return "Female", 0.7
    
    return "Unknown", 0.5

def estimate_age_from_features(face_roi_gray, face_features):
    """根据面部特征估计年龄（基于特征统计）"""
    face_height = face_roi_gray.shape[0]
    
    # 检测到的特征数量
    eye_count = len(face_features['eyes'])
    
    if eye_count >= 2:
        # 计算眼睛在脸部的位置（相对高度）
        eye_heights = [ey + eh//2 for (ex, ey, ew, eh) in face_features['eyes'][:2]]
        avg_eye_height = np.mean(eye_heights) if eye_heights else 0
        eye_position_ratio = avg_eye_height / face_height
        
        if eye_position_ratio < 0.3:
            return "Young (0-25)", 0.6
        elif eye_position_ratio < 0.4:
            return "Adult (25-50)", 0.6
        else:
            return "Senior (50+)", 0.6
    
    return "Unknown", 0.5

def detect_facial_features(face_roi_gray, cascades):
    """检测面部特征（眼睛、微笑）"""
    features = {
        'eyes': [],
        'smile_detected': False,
        'eye_count': 0,
        'feature_confidence': 0.0
    }
    
    try:
        # 检测眼睛
        eyes = cascades['eyes'].detectMultiScale(face_roi_gray, 1.1, 5)
        features['eyes'] = eyes
        features['eye_count'] = len(eyes)
        
        # 检测微笑
        if 'smile' in cascades and cascades['smile'] is not None:
            smile = cascades['smile'].detectMultiScale(face_roi_gray, 1.8, 20)
            features['smile_detected'] = len(smile) > 0
        
        # 计算特征置信度
        total_features = len(eyes) + (1 if features['smile_detected'] else 0)
        features['feature_confidence'] = min(total_features / 3.0, 1.0)
        
    except Exception as e:
        print(f"面部特征检测错误: {e}")
    
    return features

def estimate_face_orientation(face_roi_gray, eyes):
    """估计脸部朝向"""
    if len(eyes) >= 2:
        eye_centers = [(x + w//2, y + h//2) for (x, y, w, h) in eyes[:2]]
        dx = eye_centers[1][0] - eye_centers[0][0]
        dy = eye_centers[1][1] - eye_centers[0][1]
        
        # 计算眼睛连线的角度
        angle = np.degrees(np.arctan2(dy, dx))
        
        if abs(angle) < 15:
            return "Forward", 0.8
        elif angle > 15:
            return "Left", 0.7
        else:
            return "Right", 0.7
    
    return "Unknown", 0.5

def load_cascades():
    """加载所有需要的级联分类器（只使用标准分类器）"""
    cascades = {}
    
    # 基础分类器路径
    base_path = cv2.data.haarcascades
    
    # 加载标准分类器
    cascades['face'] = cv2.CascadeClassifier(base_path + 'haarcascade_frontalface_default.xml')
    cascades['eyes'] = cv2.CascadeClassifier(base_path + 'haarcascade_eye.xml')
    
    # 尝试加载微笑分类器（可能不存在）
    try:
        smile_cascade = cv2.CascadeClassifier(base_path + 'haarcascade_smile.xml')
        if not smile_cascade.empty():
            cascades['smile'] = smile_cascade
            print("微笑分类器加载成功")
        else:
            cascades['smile'] = None
            print("微笑分类器不可用")
    except Exception as e:
        print(f"微笑分类器加载失败: {e}")
        cascades['smile'] = None
    
    # 检查主要分类器是否加载成功
    for key in ['face', 'eyes']:
        if cascades[key].empty():
            print(f"错误: {key} 分类器加载失败，系统无法运行")
    
    return cascades

def analyze_faces(image_path):
    """
    分析图片中的人脸信息（使用OpenCV内置分类器）
    返回：处理后的图片路径和人脸信息列表
    """
    # 加载所有级联分类器
    cascades = load_cascades()
    
    # 检查主要分类器是否加载成功
    if cascades['face'].empty() or cascades['eyes'].empty():
        return None, []
    
    # 读取图片
    img = cv2.imread(image_path)
    if img is None:
        return None, []
    
    # 转换为灰度图
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    
    # 检测人脸
    faces = cascades['face'].detectMultiScale(
        gray,
        scaleFactor=1.1,
        minNeighbors=5,
        minSize=(30, 30),
        flags=cv2.CASCADE_SCALE_IMAGE
    )
    
    face_info = []
    
    # 定义颜色列表用于不同人脸的标记
    colors = [
        (0, 255, 0),    # 绿色
        (255, 0, 0),    # 蓝色
        (0, 0, 255),    # 红色
        (255, 255, 0),  # 青色
        (255, 0, 255),  # 紫色
        (0, 255, 255),  # 黄色
    ]
    
    # 在图片上绘制人脸框和编号
    for i, (x, y, w, h) in enumerate(faces):
        color = colors[i % len(colors)]
        
        # 绘制人脸矩形框
        cv2.rectangle(img, (x, y), (x+w, y+h), color, 3)
        
        # 提取人脸区域
        face_roi_gray = gray[y:y+h, x:x+w]
        
        # 检测面部特征
        features = detect_facial_features(face_roi_gray, cascades)
        
        # 估计性别和年龄
        gender, gender_conf = estimate_gender_from_features(face_roi_gray, features)
        age, age_conf = estimate_age_from_features(face_roi_gray, features)
        
        # 估计脸部朝向
        orientation, orientation_conf = estimate_face_orientation(face_roi_gray, features['eyes'])
        
        # 绘制眼睛
        for (ex, ey, ew, eh) in features['eyes']:
            cv2.rectangle(img, (x+ex, y+ey), (x+ex+ew, y+ey+eh), (0, 255, 255), 2)
        
        # 添加信息文本
        info_text = f'Face {i+1}: {gender}, {age}'
        cv2.putText(img, info_text, (x, y-10), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.6, color, 2)
        
        # 收集人脸信息
        face_info.append({
            'face_id': i + 1,
            'position': {
                'x': int(x),
                'y': int(y),
                'width': int(w),
                'height': int(h)
            },
            'size_ratio': round((w * h) / (img.shape[0] * img.shape[1]) * 100, 2),
            'gender': {
                'result': gender,
                'confidence': round(gender_conf * 100, 1)
            },
            'age': {
                'result': age,
                'confidence': round(age_conf * 100, 1)
            },
            'features': {
                'eyes_detected': features['eye_count'] >= 2,
                'eye_count': features['eye_count'],
                'smile_detected': features['smile_detected'],
                'orientation': orientation,
            },
            'color': f'rgb({color[0]}, {color[1]}, {color[2]})'
        })
    
    # 如果没有检测到人脸，添加提示文本
    if len(faces) == 0:
        cv2.putText(img, 'No faces detected', (50, 50), 
                   cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)
        cv2.putText(img, 'Try another image with clearer faces', (50, 80), 
                   cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 0, 255), 2)
    
    # 保存处理后的图片
    processed_filename = 'processed_' + os.path.basename(image_path)
    processed_path = os.path.join(app.config['UPLOAD_FOLDER'], processed_filename)
    cv2.imwrite(processed_path, img)
    
    return processed_filename, face_info

@app.route('/', methods=['GET', 'POST'])
def index():
    """首页 - 集成上传和分析功能"""
    if request.method == 'POST':
        # 处理文件上传
        if 'file' in request.files:
            file = request.files['file']
            if file and file.filename != '' and allowed_file(file.filename):
                # 安全保存文件名
                filename = secure_filename(file.filename)
                filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
                file.save(filepath)
                
                # 分析人脸
                processed_filename, face_info = analyze_faces(filepath)
                
                if processed_filename:
                    return render_template('index.html', 
                                         original_image=filename,
                                         processed_image=processed_filename,
                                         face_info=face_info,
                                         face_count=len(face_info),
                                         analysis_complete=True)
                else:
                    return render_template('index.html', 
                                         error="无法处理图片，请确保上传的是有效的图片文件")
        
        # 处理URL分析
        elif 'image_url' in request.form:
            image_url = request.form.get('image_url', '')
            if image_url:
                try:
                    # 从URL下载图片
                    filename = 'url_image.jpg'
                    filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
                    
                    # 下载并保存图片
                    headers = {'User-Agent': 'Mozilla/5.0'}
                    req = urllib.request.Request(image_url, headers=headers)
                    with urllib.request.urlopen(req) as response:
                        with open(filepath, 'wb') as out_file:
                            out_file.write(response.read())
                    
                    # 分析人脸
                    processed_filename, face_info = analyze_faces(filepath)
                    
                    if processed_filename:
                        return render_template('index.html', 
                                             original_image=filename,
                                             processed_image=processed_filename,
                                             face_info=face_info,
                                             face_count=len(face_info),
                                             analysis_complete=True)
                    else:
                        return render_template('index.html', error="无法处理URL图片")
                        
                except Exception as e:
                    return render_template('index.html', error=f"处理URL图片时出错: {str(e)}")
    
    # GET请求或处理完成后显示页面
    return render_template('index.html')

if __name__ == '__main__':
    print("人脸分析系统启动中...")
    print("使用OpenCV内置分类器进行人脸检测和特征分析")
    print("支持的功能: 人脸检测、眼睛检测、微笑检测、性别年龄估计")
    app.run(debug=True, host='0.0.0.0', port=5090)